﻿//1、自我介绍

//2、C++特点
面向对象编程：C++ 支持类和对象的概念，提供了封装、继承和多态等面向对象的特性。

多范式语言：除了支持面向对象编程，C++ 还支持过程式编程、泛型编程和函数式编程。

强类型语言：C++ 是强类型语言，变量必须先声明类型，再使用，并且进行类型检查。

内存管理：C++ 提供对内存的直接控制，通过 new 和 delete 操作符来动态分配和释放内存。C++ 允许开发者手动管理内存，虽然这样提高了效率，但也增加了错误的风险（如内存泄漏）。

函数重载：C++ 允许在同一作用域内定义多个同名函数，只要它们的参数类型或个数不同。这是函数重载的一种表现。

操作符重载：C++ 允许重载大部分操作符，使其对自定义类型具有特定的行为。操作符重载使得类的对象在使用时可以像基本数据类型一样直接操作。

模板：C++ 提供模板支持，允许编写类型独立的代码，可以通过模板实现泛型编程。模板支持函数模板和类模板。

STL（标准模板库）：C++ 提供了强大的标准库，包括容器、算法和迭代器，使得常见的数据结构和算法更加易于实现和使用。

高效的编译：C++ 是一种编译型语言，代码在运行前被编译为机器码，这使得其执行效率较高。

跨平台性：C++ 程序可以在不同操作系统和平台上编译运行，尽管跨平台开发需要一定的代码适配，但仍然具有较强的可移植性。

异常处理：C++ 支持异常处理，通过 try、catch 和 throw 来捕获和处理错误，增强程序的健壮性。

//3、C++内存分配函数
malloc() calloc() realloc()
free()
new
delete
new[]
delete[]
placement new(定位new)   允许在已分配的内存上构造对象，常用于 内存池 或 自定义分配器。
#include <new>

char buffer[sizeof(int)];  // 预留空间
int* p = new (buffer) int(123); // 在 buffer 中构造 int 对象

使用 placement new 需要手动调用析构函数：p->~int();


//4、new和delete的区别
关键字	作用	     是否调用构造 / 析构函数	需要指定对象类型	返回类型
new	    分配内存	✅ 调用构造函数	            ✅ 需要类型	        具体类型指针
delete	释放内存	✅ 调用析构函数	            ✅ 需要类型	        无返回值


//5、说一下高并发的服务器，如何实现一个高并发服务器     
多进程模式
多线程模式
事件驱动模型（select/poll/epoll）
负载均衡

//6、epoll和poll的区别
原理：
select 通过 遍历整个文件描述符集合 来检查是否有 I / O 事件发生。
用户需要在 fd_set 中手动添加所有要监听的文件描述符。
select 会 阻塞 直到有 I / O 事件发生，或超时返回。
特点：
最大支持 1024 个文件描述符（Linux 默认 FD_SETSIZE = 1024）。
性能低下：每次调用 select 都需要遍历所有 fd，大并发下效率极低。


原理：
poll 通过 pollfd 数组 维护所有监听的文件描述符。
poll 没有 select 的 fd 限制（可监听的 fd 数量取决于系统）。
仍然是遍历所有 fd，效率不高。
特点：
解决了 select 的 1024 限制，但仍然遍历整个列表，大并发下性能低。


原理：
epoll 通过 事件驱动（event - driven）机制，避免了 select 和 poll 逐个扫描 fd 的问题。
内核提供 epoll_ctl() 进行 增删改，不会每次都遍历整个 fd 集合。
epoll_wait() 只返回 发生事件的 fd，大幅提升性能。
特点：
无 fd 数量限制（理论支持百万级别）。
只关心有事件发生的 fd，不遍历整个 fd 集合。
支持边缘触发（ET）和水平触发（LT），更高效。

特性					select							poll						epoll
监听方式				维护 fd_set						维护 pollfd 数组			内核事件驱动
fd 限制					1024（Linux 默认）				无固定限制					无限制
遍历方式				每次遍历所有 fd					每次遍历所有 fd				只返回发生事件的 fd
性能					低（O(n) 复杂度）				低（O(n) 复杂度）			高（O(1) 复杂度）
适用场景				少量连接（如 100 个以内）		中等连接（如 1000 个）		大并发（如 10 万 + 连接）
是否支持 ET（边缘触发）	❌ 不支持						❌ 不支持					✅ 支持
应用场景				早期 Unix 服务器				轻量级并发服务器			高并发服务器（Nginx、Redis）


epoll 的两种触发模式
(1) 水平触发（LT，Level Triggered，默认）
特点：只要 fd 还有数据未读取，每次 epoll_wait() 都会返回该 fd。
适用场景：适用于大部分服务器，如 Web 服务器。
event.events = EPOLLIN; // 默认 LT 模式

(2) 边缘触发（ET，Edge Triggered）
特点：
只有新数据到达时，epoll_wait() 才返回事件。
需要一次性读取完所有数据，否则会丢失通知。
适用场景：适用于高性能应用（如 Nginx）。
注意事项：ET 方式下，必须用 non - blocking 模式读取数据，否则可能会死锁。

高并发服务器（如 Nginx、Redis）一般采用：
epoll 事件驱动（ET 模式）
非阻塞 I / O
结合线程池或协程优化


//7、测试过服务器的并发有多少吗？怎么测
测试工具：
小规模测试：ab
高并发测试（推荐）：wrk
压力测试：siege
网络测试：iperf
服务器优化：
使用 epoll（高并发）
优化 TCP 连接
数据库连接池
缓存热点数据
负载均衡（Nginx + Keepalived）


//8、gdb调试
debug模式下  编译时加上 -g
run
quit
b
d
dispaly
undispaly
bt
info b
n
s
i
p
finish
c
until 
disable
enable


//9、gdb多线程调试
在 GDB（GNU Debugger） 中调试多线程程序需要掌握以下关键点：

查看线程列表
切换线程
设置断点
单步调试
观察线程运行状态

info threads  （查看线程列表）
thread x（ID） (切换线程)
break my_function   如果 my_function 在多个线程中执行，每个线程运行到该函数时都会触发断点。
break my_function thread 2    如果只想让 线程 2 触发断点：

stepi  逐语句但是不进入函数


thread apply all bt  查看所有线程的调用栈



thread 2   有时候你想让某个线程暂停，但其他线程继续运行，可以使用：
interrupt

thread 2   如果想终止 线程 2：
kill

//10、线程  Vs  进程

特性					进程（Process）					线程（Thread）
资源分配单位			进程独占资源					线程共享进程资源
通信方式				需要 IPC 机制					共享内存
切换开销				高								低
稳定性					进程独立，稳定性高				线程可能影响整个进程
适用场景				多个独立任务					高并发、多任务处理



//11、多线程访问同一空间需要注意什么（锁机制详解）
std::mutex				普通互斥锁					线程同步
std::lock_guard			自动管理锁				RAII 风格管理
std::unique_lock		手动管理锁				条件变量、手动解锁
std::shared_mutex		读写锁					读多写少场景
std::timed_mutex		超时获取锁				防止线程长时间等待
自旋锁				线程不睡眠，消耗 CPU		锁时间短的高并发场景



//12、锁的种类   
互斥锁       一次只允许一个线程访问共享资源       适用于对共享资源的独占式访问，如对全局变量、文件操作等       
实现简单  如果一个线程长时间持有锁，会导致其他线程长时间等待，可能引起性能问题，甚至可能出现死锁情况。

读写锁  将对共享资源的访问分为读操作和写操作。允许多个线程同时进行读操作，但在写操作时需要独占资源，即当有一个线程进行写操作时，其他读线程和写线程都需要等待。
适用于读操作远多于写操作的场景，如缓存数据的访问
提高了读操作的并发性，在多读少写的场景下能显著提升性能。  实现相对复杂，需要考虑读写操作的优先级、线程饥饿等问题。

自旋锁   当一个线程尝试获取锁时，如果锁已经被其他线程占用，那么该线程不会进入阻塞状态，而是会在原地不断循环检查锁是否可用，直到获取到锁为止。
适用于锁的持有时间非常短，且线程上下文切换成本较高的场景
优点：没有线程切换的开销，获取锁的速度快。
缺点：会消耗 CPU 资源，若锁长时间被占用，会导致自旋的线程浪费大量 CPU 时间。



//13、其他保证线程安全的方法
原子操作
线程本地存储
不可变对象
消息队列
信号量

14、socket编程流程（tcp）
server:
创建套接字（socket）
绑定地址和端口（bind）
监听连接（listen）
接受连接（accept）
收发数据（send / recv）
关闭连接（close）


client：
创建套接字（socket）
连接服务器（connect）  在这一步实际上已经进行了绑定（bind）操作
收发数据（send / recv）
关闭连接（close）

udp 通信
服务器端和客户端不需要进行连接操作   直接使用socket进行数据的发送和接收
直接使用socket进行数据的发送和接收。

server
recvfrom()接收数据并获取客户端地址，使用sendto()向指定地址发送数据；

client
sendto()发送数据，使用recvfrom()接收数据


//14、什么是粘包问题？怎么解决粘包问题
粘包：发送方连续发送的多个数据包可能会被合并成一个数据包发送到接收方，接收方无法区分这些原始数据包的边界。
这主要是因为 TCP 是面向流的协议，它会将应用层的数据看作是无边界的字节流，在发送时会根据缓冲区的情况进行优化，可能会将多个小的数据包合并成一个大的数据包发送。

拆包：一个完整的数据包可能会被拆分成多个小的数据包进行传输，接收方需要重新组合这些小数据包才能得到完整的数据。
这通常是由于网络传输的 MTU（最大传输单元）限制，当数据包的大小超过 MTU 时，就会被拆分成多个小的数据包。


规定每个数据包的长度是固定的。发送方在发送数据时，如果数据长度不足固定长度，会进行填充；接收方按照固定长度来接收和解析数据。
在每个数据包的末尾添加一个特殊的分隔符，接收方根据分隔符来区分不同的数据包。
在每个数据包的前面添加一个消息头，消息头中包含消息体的长度信息。接收方先接收消息头，解析出消息体的长度，然后根据长度接收完整的消息体。
采用成熟的应用层协议，如 HTTP、MQTT 等，这些协议已经对数据的传输和解析进行了规范，能够很好地解决粘包拆包问题。


15、TCP和udp的区别  怎么查看tcp的连接
用户数据报协议 UDP（User Datagram Protocol）

是无连接的，尽最大可能交付，没有拥塞控制，面向报文（对于应用程序传下来的报文不合并也不拆分，只是添加 UDP 首部），支持一对一、一对多、多对一和多对多的交互通信。

传输控制协议 TCP（Transmission Control Protocol）

是面向连接的，提供可靠交付，有流量控制，拥塞控制，提供全双工通信，
面向字节流（把应用层传下来的报文看成字节流，把字节流组织成大小不等的数据块），
每一条 TCP 连接只能是点对点的（一对一）。

netstat - tunlp
其中 - t表示显示 TCP 连接， - u表示显示 UDP 连接， - n表示以数字形式显示地址和端口号， - l表示显示监听状态的连接， - p表示显示进程名称和 PID。
ss - tunlp
ss - tunlp命令也可以用来查看 TCP 连接，功能与netstat类似，但ss命令的性能更好，能够更快地显示结果。





//15、mysql索引介绍（详细）  索引的规则   什么字段适合建索引
定义：索引是一种数据结构，用于提高数据库中数据的查询效率。它就像一本书的目录，通过特定的键值可以快速定位到数据所在的位置，而不必全表扫描。

作用
快速定位数据：可以大大减少查询时需要扫描的数据量，从而提高查询速度。
保证数据唯一性：某些索引类型可以确保表中的数据具有唯一性，例如主键索引。
支持数据排序：在查询时，如果需要对数据进行排序，索引可以帮助数据库快速找到排序后的数据，而不需要额外的排序操作。

常见类型
B - tree 索引：是 MySQL 中最常用的索引类型，适用于全键值、键值范围和键前缀查找。B - tree 索引可以快速定位到满足条件的数据页，然后在数据页中进行进一步的查找。
哈希索引：基于哈希表实现，通过对键值进行哈希运算来快速定位数据。哈希索引只能进行等值查询，不支持范围查询和排序操作。
全文索引：用于对文本类型的数据进行全文搜索，它可以快速找到包含特定关键词的记录。
空间索引：用于存储和查询空间数据类型，如点、线、面等。

索引的规则
创建索引的语法：在 MySQL 中，可以使用CREATE INDEX语句来创建索引，例如CREATE INDEX index_name ON table_name(column_name); 。也可以在创建表时使用CREATE TABLE语句来创建索引，例如CREATE TABLE table_name(column1 datatype, column2 datatype, INDEX index_name(column1)); 。
索引的命名规范：索引名应具有描述性，能清晰地反映出索引所关联的表和列。一般遵循一定的命名约定，比如以idx_开头，后面跟着表名和列名，如idx_users_name。
索引的列顺序：当创建多列索引时，列的顺序很重要。一般来说，将选择性高（即不同值较多）的列放在前面，这样可以提高索引的效率。
索引的维护：当表中的数据发生插入、更新或删除操作时，MySQL 会自动维护索引，确保索引的正确性和有效性。但这也会带来一定的性能开销，因此在设计索引时需要权衡查询性能和维护成本。
索引的限制：每个表的索引数量有限，过多的索引会占用大量的磁盘空间，并且会降低数据插入、更新和删除的性能。同时，索引列的数据类型也有限制，某些数据类型可能不适合创建索引，或者创建索引的效果不佳。

适合建索引的字段
主键字段：主键是表中用于唯一标识每条记录的字段，MySQL 会自动为主键创建索引，以保证数据的唯一性和快速访问。
外键字段：外键用于建立表与表之间的关联关系，在关联查询时，外键字段上的索引可以提高查询效率。
经常用于查询条件的字段：如果某个字段经常在WHERE子句中用于过滤数据，那么在该字段上创建索引可以大大提高查询速度。
经常用于连接操作的字段：在多表连接查询中，用于连接的字段通常应该创建索引，这样可以加快连接操作的速度。
经常用于排序和分组的字段：当需要对数据进行排序或分组时，在相应的字段上创建索引可以避免额外的排序操作，提高查询性能。
高选择性的字段：选择性是指字段中不同值的数量与记录总数的比例。选择性越高，索引的效果越好。例如，性别字段的选择性较低，而身份证号码字段的选择性较高，后者更适合创建索引。